/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DouEditor.Const;
using DouEditor.Utils;
using UnityEditor;
using UnityEngine;

namespace DouEditor.Resource
{
    public static class ShaderVariantToolkit
    {
        /// <summary>
        /// 打包着色器变体收集步骤
        /// 1. 初始化或需要更新时，执行当前指令，创建带有全部材质使用的关键字的材质；
        /// 2. 配置动态设定的着色器关键字；
        /// 3. 执行打包，打包会收集该指令创建的材质的着色器及关键字，以及动态设定的着色器及关键字；
        /// </summary>
        [MenuItem("DouFramework/资源/创建用于收集所有变体的材质", false, 10000)]
        public static void CreateShaderVariantMaterial()
        {
            if (!EditorUtility.DisplayDialog("提示", "创建用于收集所有变体的材质？", "确定", "取消"))
            {
                return;
            }
            
            EditorUtil.CreateDir(BuildConst.ShaderVariantMaterialCreatePath);
            if (!File.Exists(BuildConst.DynamicShaderVariantFilePath))
            {
                var dynamicShaderVariant = ScriptableObject.CreateInstance<DynamicShaderVariant>();
                AssetDatabase.CreateAsset(dynamicShaderVariant, BuildConst.DynamicShaderVariantFilePath);
                AssetDatabase.Refresh();
            }

            var shaderKeywordsMap = new Dictionary<Shader, HashSet<string>>();
            
            // 这里直接全项目扫描，全项目扫描可能会加入未入包使用的变体，但是肯定不会导致出包后出现变体丢失的异常
            // 如果使用CollectDepends一样的方式仅扫描打入AB包的材质，又可能会漏掉Resource内部的文件的引用
            var allAssetPaths = AssetDatabase.GetAllAssetPaths();
            foreach (var assetPath in allAssetPaths)
            {
                if (assetPath.EndsWith(".mat"))
                {
                    var mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
                    if (mat == null || mat.shader == null)
                    {
                        Debug.LogError($"材质\"{assetPath}\"异常！");
                        continue;
                    }
                    var shader = mat.shader;
                    var keywords = mat.shaderKeywords;
                    if (!shaderKeywordsMap.ContainsKey(shader))
                    {
                        shaderKeywordsMap.Add(shader, new HashSet<string>());
                    }
                    var keywordsSet = shaderKeywordsMap[shader];
                    foreach (var keyword in keywords)
                    {
                        keywordsSet.Add(keyword);
                    }
                }
            }
            
            // 加入动态配置的关键字
            var dynamicConfig = AssetDatabase.LoadAssetAtPath<DynamicShaderVariant>(BuildConst.DynamicShaderVariantFilePath);
            foreach (var item in dynamicConfig.items)
            {
                if (!shaderKeywordsMap.ContainsKey(item.shader))
                {
                    shaderKeywordsMap.Add(item.shader, new HashSet<string>());
                }
                var keywordsSet = shaderKeywordsMap[item.shader];
                foreach (var keyword in item.keywords)
                {
                    keywordsSet.Add(keyword);
                }
            }
            
            // 需要创建的材质
            var files = Directory.GetFiles(BuildConst.ShaderVariantMaterialCreatePath, "*.mat", SearchOption.TopDirectoryOnly);
            foreach (var file in files)
            {
                AssetDatabase.DeleteAsset(file);
            }
            AssetDatabase.Refresh();

            foreach (var kv in shaderKeywordsMap)
            {
                var shader = kv.Key;
                var keywords = kv.Value;
                var fileName = Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(shader)) + "_variant";
                
                var shaderKeywords = keywords.ToArray();
                Array.Sort(shaderKeywords);
                
                var material = new Material(shader)
                {
                    shaderKeywords = shaderKeywords,
                    name = fileName
                };
                AssetDatabase.CreateAsset(material, Path.Combine(BuildConst.ShaderVariantMaterialCreatePath, fileName + ".mat"));
            }
            AssetDatabase.Refresh();

            EditorUtility.DisplayDialog("提示", "创建用于收集所有变体的材质完毕！", "确定");
        }
    }
}
